﻿using Common.AspNetCore;
using iTextSharp.text;
using iTextSharp.text.pdf;
using iTextSharp.text.pdf.parser;
using NPOI.HSSF.UserModel;
using NPOI.HSSF.Util;
using NPOI.SS.UserModel;
using NPOI.SS.Util;
using NPOI.XSSF.UserModel;
using Org.BouncyCastle.Asn1.Ocsp;
using SixLabors.ImageSharp;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace File.Api.Service
{
    public class PdfService : CommonService
    {
        ////设置pdf内容占文档的宽度比例
        private float widthPercent = 88;
        ////设置pdf是否横向
        private bool isLandscape = true;
        ////使itextsharp支持中文
        private string fontPath = @"C:\Windows\Fonts\simhei.ttf";
        private float marginTop = 15;
        private float marginBottom = 15;
        ////设置pdf文档纸张大小   
        private iTextSharp.text.Rectangle pageSize = PageSize.A4;                                                        ////以上设置均可在配置文件中进行设置，在此就不介绍

        public PdfService(IServiceProvider provider) : base(provider)
        {
        }

        ////public PdfService(float widthPercent, bool isLandscape)
        ////{
        ////    this.widthPercent = widthPercent;
        ////    this.isLandscape = isLandscape;
        ////}

        ///// <summary>
        ///// 生成pdf文件
        ///// </summary>
        ///// <param name="excelContent"></param>
        ///// <returns></returns>
        public byte[] Render(byte[] excelContent)
        {
            if (excelContent == null)
            {
                return null;
            }

            byte[] result = null;
            MemoryStream stream = new MemoryStream(excelContent);
            XSSFWorkbook hw = new XSSFWorkbook(stream);
            Document doc;
            if (isLandscape)
            {
                doc = new Document(pageSize.Rotate());
            }
            else
            {
                doc = new Document(pageSize);
            }

            var path = System.IO.Path.Combine(AppContext.BaseDirectory, "Fonts");
            doc.SetMargins(0, 0, marginTop, marginBottom);
            try
            {
                ISheet sheet = hw.GetSheetAt(0);
                stream = new MemoryStream();
                PdfWriter pdfWriter = PdfWriter.GetInstance(doc, stream);
                Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
                BaseFont bsfont = BaseFont.CreateFont(System.IO.Path.Combine(path, "simhei.ttf"), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
                ////创建pdf文档字体
                doc.Open();
                float[] widths = GetColWidth(sheet);
                PdfPTable table = new PdfPTable(widths);
                table.WidthPercentage = widthPercent;
                int colCount = widths.Length;
                int tt = sheet.FirstRowNum;
                int ccc = 0;
                for (int r = sheet.FirstRowNum; r < sheet.PhysicalNumberOfRows; r++)
                {
                    IRow row = sheet.GetRow(r);
                    if (row != null)
                    {
                        for (int c = row.FirstCellNum; (c < row.PhysicalNumberOfCells || c < colCount) && c > -1; c++)
                        {
                            if (c >= row.PhysicalNumberOfCells)
                            {
                                PdfPCell cell = new PdfPCell(new Phrase(string.Empty));
                                cell.Border = 0;
                                table.AddCell(cell);
                                continue;
                            }

                            ICell excelCell = row.Cells[c];
                            string value = string.Empty;
                            string horAlign = excelCell.CellStyle.Alignment.ToString();
                            string verAlign = excelCell.CellStyle.VerticalAlignment.ToString();

                            if (excelCell != null)
                            {
                                value = excelCell.ToString().Trim();
                                if (!string.IsNullOrEmpty(value))
                                {
                                    string dataFormat = excelCell.CellStyle.GetDataFormatString();
                                    ////数据不为常规或者文本
                                    if (dataFormat != "General" && dataFormat != "@")
                                    {
                                        try
                                        {
                                            ////如果解析不成功则按字符串处理
                                            string numStyle = GetNumStyle(dataFormat);
                                            value = string.Format("{0}:numStyle", excelCell.NumericCellValue);
                                        }
                                        catch
                                        {
                                        }
                                    }
                                }
                            }

                            IFont excelFont = excelCell.CellStyle.GetFont(hw);
                            ////HSSFPalette palette = hw.GetCustomPalette();
                            ////HSSFColor color = null;
                            ////BaseColor ftcolor = BaseColor.BLACK;
                            ////short ft = excelFont.Color;
                            ////color = palette.GetColor(ft);
                            ////if (color != null && ft != 64)
                            ////{
                            ////    byte[] ftrgb = color.RGB;
                            ////    ftcolor = new BaseColor(ftrgb[0], ftrgb[1], ftrgb[2]);
                            ////}

                            bool isBorder = HasBorder(excelCell);
                            float dloatFont = float.Parse(excelFont.FontHeightInPoints.ToString());
                            Font pdfFont = new Font(bsfont, dloatFont);
                            PdfPCell pdfCell = new PdfPCell(new Phrase(value, pdfFont));

                            ////////List<PicturesInfo> info = sheet.GetAllPictureInfos(r, r, c, c, true);////判断单元格中是否有图片，不支持图片跨单元格
                            ////////if (info.Count > 0)
                            ////////{
                            ////////    pdfCell = new PdfPCell(System.Net.Mime.MediaTypeNames.Image.GetInstance(info[0].PictureData));
                            ////////}
                            ///short bg = excelCell.CellStyle.FillForegroundColor;
                            ////color = palette.GetColor(bg);
                            ////if (color != null && bg != 64)
                            ////{
                            ////    byte[] bgrgb = color.RGB;
                            ////    pdfCell.BackgroundColor = new BaseColor(bgrgb[0], bgrgb[1], bgrgb[2]);
                            ////}

                            ////if (!isBorder)
                            ////{
                            ////    pdfCell.Border = 0;
                            ////}
                            ////else
                            ////{
                            ////    short bd = excelCell.CellStyle.TopBorderColor;
                            ////    ////color = palette.GetColor(bd);
                            ////    ////if (color != null && bd != 64)
                            ////    ////{
                            ////    ////    byte[] bdrgb = color.RGB;
                            ////    ////    pdfCell.BorderColor = new BaseColor(bdrgb[0], bdrgb[1], bdrgb[2]);
                            ////    ////}
                            ////}

                            pdfCell.MinimumHeight = row.HeightInPoints;
                            pdfCell.HorizontalAlignment = GetCellHorAlign(horAlign);
                            pdfCell.VerticalAlignment = GetCellVerAlign(verAlign);
                            ////if (tt == 0 || tt == 1)
                            ////{
                            ////    pdfCell.HorizontalAlignment = GetCellHorAlign("Center");
                            ////    pdfCell.VerticalAlignment = GetCellVerAlign("Center");
                            ////    pdfCell.Border = 1;
                            ////}

                            if (excelCell.IsMergedCell)
                            {
                                int[] span = GetMergeCellSpan(sheet, r, c, ccc);
                                ////合并过的单元直接跳过
                                if (span[0] == 1 && span[1] == 1)
                                {
                                    continue;
                                }

                                pdfCell.Rowspan = span[0];
                                pdfCell.Colspan = span[1];
                                ////直接跳过合并过的单元格
                                c = c + span[1] - 1;
                            }

                            table.AddCell(pdfCell);
                        }

                        tt++;
                    }
                    else
                    {
                        ////空行
                        PdfPCell pdfCell = new PdfPCell(new Phrase(string.Empty));
                        pdfCell.Border = 0;
                        pdfCell.MinimumHeight = 13;
                        table.AddCell(pdfCell);
                    }
                }

                doc.Add(table);
                doc.Close();
                result = stream.ToArray();
            }
            finally
            {
                hw.Close();
                stream.Close();
            }

            return result;
        }

        ///// <summary>
        ///// 获取单元格的数字格式
        ///// </summary>
        ///// <param name="style"></param>
        ///// <returns></returns>
        private string GetNumStyle(string style)
        {
            if (string.IsNullOrEmpty(style))
            {
                throw new ArgumentException(string.Empty);
            }

            if (style.IndexOf('%') > -1)
            {
                return style;
            }
            else
            {
                return style.Substring(0, style.Length - 2);
            }
        }

        ///// <summary>
        ///// 获取列的宽度比例
        ///// </summary>
        ///// <param name="sheet"></param>
        ///// <returns></returns>
        private float[] GetColWidth(ISheet sheet)
        {
            int rowNum = GetMaxColRowNum(sheet);
            IRow row = sheet.GetRow(rowNum);
            int cellCount = row.PhysicalNumberOfCells;
            int[] colWidths = new int[cellCount];
            float[] colWidthPer = new float[cellCount];

            int sum = 0;
            for (int i = row.FirstCellNum; i < cellCount; i++)
            {
                ICell cell = row.Cells[i];
                if (cell != null)
                {
                    colWidths[i] = sheet.GetColumnWidth(i);
                    sum += sheet.GetColumnWidth(i);
                }
            }

            for (int i = row.FirstCellNum; i < cellCount; i++)
            {
                colWidthPer[i] = (float)colWidths[i] / sum * 100;
            }

            return colWidthPer;
        }

        ///// <summary>
        ///// 取EXCEL中列数最大的行
        ///// </summary>
        ///// <param name="sheet"></param>
        ///// <returns></returns>
        private int GetMaxColRowNum(ISheet sheet)
        {
            int rowNum = 0;
            int maxCol = 0;
            for (int r = sheet.FirstRowNum; r < sheet.PhysicalNumberOfRows; r++)
            {
                IRow row = sheet.GetRow(r);
                if (row != null && maxCol < row.PhysicalNumberOfCells)
                {
                    maxCol = row.PhysicalNumberOfCells;
                    rowNum = r;
                }
            }

            return rowNum;
        }

        ///// <summary>
        ///// 单元格水平对齐方式
        ///// </summary>
        ///// <param name="align"></param>
        ///// <returns></returns>
        private int GetCellHorAlign(string align)
        {
            switch (align)
            {
                case "Right":
                    return Element.ALIGN_RIGHT;
                case "Center":
                    return Element.ALIGN_CENTER;
                case "Left":
                    return Element.ALIGN_LEFT;
                default:
                    return Element.ALIGN_LEFT;
            }
        }

        ///// <summary>
        ///// 单元格垂直对齐方式
        ///// </summary>
        ///// <param name="align"></param>
        ///// <returns></returns>
        private int GetCellVerAlign(string align)
        {
            switch (align)
            {
                case "Center":
                    return Element.ALIGN_MIDDLE;
                case "Top":
                    return Element.ALIGN_TOP;
                case "Bottom":
                    return Element.ALIGN_BOTTOM;
                default:
                    return Element.ALIGN_MIDDLE;
            }
        }

        private bool HasBorder(ICell cell)
        {
            int bottom = cell.CellStyle.BorderBottom != 0 ? 1 : 0;
            int top = cell.CellStyle.BorderTop != 0 ? 1 : 0;
            int left = cell.CellStyle.BorderLeft != 0 ? 1 : 0;
            int right = cell.CellStyle.BorderRight != 0 ? 1 : 0;
            return (bottom + top + left + right) > 2;
        }

        ///// <summary>
        ///// 合并单元格的rowspan、colspan
        ///// </summary>
        ///// <param name="sheet"></param>
        ///// <param name="rowNum"></param>
        ///// <param name="colNum"></param>
        ///// <returns></returns>
        private int[] GetMergeCellSpan(ISheet sheet, int rowNum, int colNum, int ct)
        {
            int[] span = { 1, 1 };
            int regionsCount = sheet.NumMergedRegions;
            var tt = sheet.MergedRegions.FirstOrDefault(p => p.FirstRow == rowNum && p.FirstColumn == colNum);
            if (tt != null)
            {
                span[0] = tt.LastRow - tt.FirstRow + 1;
                span[1] = tt.LastColumn - tt.FirstColumn + 1;
            }

            ////for (int i = 0; i < regionsCount; i++)
            ////{
            ////    CellRangeAddress range = sheet.GetMergedRegion(i);
            ////    var isrange = sheet.IsMergedRegion(range);
            ////    if (isrange && range.FirstRow == rowNum && range.FirstColumn == colNum)
            ////    {
            ////        ////当Excel中 合并多行数据不加这一行导出pdf格式无错  没有合并只是一条数据的时候 不加这一个if判断数据会错乱 有的会消失
            ////        ////if (range.LastRow != range.FirstRow)
            ////        ////{
            ////        ////    span[0] = 0;
            ////        ////    span[1] = 1;
            ////        ////    break;
            ////        ////}

            ////        span[0] = range.LastRow - range.FirstRow + 1;
            ////        span[1] = range.LastColumn - range.FirstColumn + 1;
            ////        break;
            ////    }
            ////}

            return span;
        }
    }

    ////单元格中图片信息类
    ////public class PicturesInfo
    ////{
    ////    public int MinRow { get; set; }
    ////    public int MaxRow { get; set; }
    ////    public int MinCol { get; set; }
    ////    public int MaxCol { get; set; }
    ////    public string Mime { get; set; }
    ////    public Byte[] PictureData { get; private set; }

    ////    public PicturesInfo(int minRow, int maxRow, int minCol, int maxCol, Byte[] pictureData, string mime)
    ////    {
    ////        this.MinRow = minRow;
    ////        this.MaxRow = maxRow;
    ////        this.MinCol = minCol;
    ////        this.MaxCol = maxCol;
    ////        this.PictureData = pictureData;
    ////        this.Mime = mime;
    ////    }
    ////}

    ////NPOI扩展方法类

    ////public static class NPOIExtend
    ////{
    ////    public static List<PicturesInfo> GetAllPictureInfos(this ISheet sheet)
    ////    {
    ////        return sheet.GetAllPictureInfos(null, null, null, null, true);
    ////    }

    ////    public static List<PicturesInfo> GetAllPictureInfos(this ISheet sheet, int? minRow, int? maxRow, int? minCol, int? maxCol, bool onlyInternal)
    ////    {
    ////        if (sheet is HSSFSheet)
    ////        {
    ////            return GetAllPictureInfos((HSSFSheet)sheet, minRow, maxRow, minCol, maxCol, onlyInternal);
    ////        }
    ////        else if (sheet is XSSFSheet)
    ////        {
    ////            return GetAllPictureInfos((XSSFSheet)sheet, minRow, maxRow, minCol, maxCol, onlyInternal);
    ////        }
    ////        else
    ////        {
    ////            throw new Exception("未处理类型，没有为该类型添加：GetAllPicturesInfos()扩展方法！");
    ////        }
    ////    }

    ////    private static List<PicturesInfo> GetAllPictureInfos(HSSFSheet sheet, int? minRow, int? maxRow, int? minCol, int? maxCol, bool onlyInternal)
    ////    {
    ////        List<PicturesInfo> picturesInfoList = new List<PicturesInfo>();

    ////        var shapeContainer = sheet.DrawingPatriarch as HSSFShapeContainer;
    ////        if (null != shapeContainer)
    ////        {
    ////            var shapeList = shapeContainer.Children;
    ////            foreach (var shape in shapeList)
    ////            {
    ////                if (shape is HSSFPicture && shape.Anchor is HSSFClientAnchor)
    ////                {
    ////                    var picture = (HSSFPicture)shape;
    ////                    var anchor = (HSSFClientAnchor)shape.Anchor;

    ////                    if (IsInternalOrIntersect(minRow, maxRow, minCol, maxCol, anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, onlyInternal))
    ////                    {
    ////                        picturesInfoList.Add(new PicturesInfo(anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, picture.PictureData.Data, picture.PictureData.MimeType));
    ////                    }
    ////                }
    ////            }
    ////        }

    ////        return picturesInfoList;
    ////    }

    ////    private static List<PicturesInfo> GetAllPictureInfos(XSSFSheet sheet, int? minRow, int? maxRow, int? minCol, int? maxCol, bool onlyInternal)
    ////    {
    ////        List<PicturesInfo> picturesInfoList = new List<PicturesInfo>();

    ////        var documentPartList = sheet.GetRelations();
    ////        foreach (var documentPart in documentPartList)
    ////        {
    ////            if (documentPart is XSSFDrawing)
    ////            {
    ////                var drawing = (XSSFDrawing)documentPart;
    ////                var shapeList = drawing.GetShapes();
    ////                foreach (var shape in shapeList)
    ////                {
    ////                    if (shape is XSSFPicture)
    ////                    {
    ////                        var picture = (XSSFPicture)shape;
    ////                        var anchor = picture.GetPreferredSize();

    ////                        if (IsInternalOrIntersect(minRow, maxRow, minCol, maxCol, anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, onlyInternal))
    ////                        {
    ////                            picturesInfoList.Add(new PicturesInfo(anchor.Row1, anchor.Row2, anchor.Col1, anchor.Col2, picture.PictureData.Data, picture.PictureData.MimeType));
    ////                        }
    ////                    }
    ////                }
    ////            }
    ////        }

    ////        return picturesInfoList;
    ////    }

    ////    private static bool IsInternalOrIntersect(int? rangeMinRow, int? rangeMaxRow, int? rangeMinCol, int? rangeMaxCol,
    ////        int pictureMinRow, int pictureMaxRow, int pictureMinCol, int pictureMaxCol, bool onlyInternal)
    ////    {
    ////        int _rangeMinRow = rangeMinRow ?? pictureMinRow;
    ////        int _rangeMaxRow = rangeMaxRow ?? pictureMaxRow;
    ////        int _rangeMinCol = rangeMinCol ?? pictureMinCol;
    ////        int _rangeMaxCol = rangeMaxCol ?? pictureMaxCol;

    ////        if (onlyInternal)
    ////        {
    ////            return (_rangeMinRow <= pictureMinRow && _rangeMaxRow >= pictureMaxRow &&
    ////                    _rangeMinCol <= pictureMinCol && _rangeMaxCol >= pictureMaxCol);
    ////        }
    ////        else
    ////        {
    ////            return ((Math.Abs(_rangeMaxRow - _rangeMinRow) + Math.Abs(pictureMaxRow - pictureMinRow) >= Math.Abs(_rangeMaxRow + _rangeMinRow - pictureMaxRow - pictureMinRow)) &&
    ////            (Math.Abs(_rangeMaxCol - _rangeMinCol) + Math.Abs(pictureMaxCol - pictureMinCol) >= Math.Abs(_rangeMaxCol + _rangeMinCol - pictureMaxCol - pictureMinCol)));
    ////        }
    ////    }

    ////}
}
